package com.fuyun.microservice;

import com.fuyun.microservice.apolloclient.EnableApolloConfigs;
import com.fuyun.microservice.connect.CodecChannelInitializer;
import com.fuyun.microservice.connect.EventLoopGroupFactory;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.Channel;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelOption;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.logging.LogLevel;
import io.netty.handler.logging.LoggingHandler;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.netflix.feign.EnableFeignClients;

import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import java.net.InetSocketAddress;

@SpringBootApplication
@Slf4j
@EnableDiscoveryClient
@EnableFeignClients
@EnableApolloConfigs({"gate-socket"})
public class ServerLauncher   {
    public static void main(String[] args) {
        SpringApplication springApplication= new SpringApplication(ServerLauncher.class);
        springApplication.run(args);
    }

    @Value("${netty.port:10023}")
    int port;

    private Channel acceptorChannel = null;

    @PostConstruct
    public void startup() throws InterruptedException {
        ServerBootstrap bootstrap = initServerBootstrap();
        ChannelFuture future = bootstrap.bind().sync();
        acceptorChannel = future.channel();
        if (future.isSuccess()) {
            log.info("gate server started on port:{}",port);
        }

        Runtime.getRuntime().addShutdownHook(new Thread(() -> close()));
    }

    @PreDestroy
    public void close() {
        log.info("gate server closed");
        EventLoopGroupFactory.getBoss().shutdownGracefully().syncUninterruptibly();
        EventLoopGroupFactory.getWorker().shutdownGracefully().syncUninterruptibly();
        if (acceptorChannel != null) {
            acceptorChannel.close().syncUninterruptibly();
        }
    }

    @Autowired
    CodecChannelInitializer channelInitializer;


    private ServerBootstrap initServerBootstrap() {
        return new ServerBootstrap()
                .group(EventLoopGroupFactory.getBoss(), EventLoopGroupFactory.getWorker())
                .channel(NioServerSocketChannel.class)
                .localAddress(new InetSocketAddress(port))
                //服务端可连接队列数,对应TCP/IP协议listen函数中backlog参数
                .option(ChannelOption.SO_BACKLOG, 1024)
                .option(ChannelOption.SO_RCVBUF, 2048)
                .option(ChannelOption.SO_SNDBUF, 2048)
                .handler(new LoggingHandler(LogLevel.INFO))
                //设置TCP长连接,一般如果两个小时内没有数据的通信时,TCP会自动发送一个活动探测数据报文
                .childOption(ChannelOption.SO_KEEPALIVE, true)
                //将小的数据包包装成更大的帧进行传送，提高网络的负载,即TCP延迟传输
                .childOption(ChannelOption.TCP_NODELAY, true)
                .childHandler(channelInitializer);
    }
}
